﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using Neo4jRestSharp.Neo4jJsonServices;
using Neo4jRestSharp.Neo4jRestServices;
using Newtonsoft.Json.Linq;

namespace Neo4jRestSharp.Neo4jObjectServices
{	
    /// <summary>
    /// Rest API for Neo4j as specified at (http://components.neo4j.org/neo4j-rest/)
    /// </summary>
    public class NodeObjectServices : IObjectServices
    {
        private readonly Uri uri;
        private NodeJsonServices jsonServices;    	
        public NodeObjectServices(Uri uri)
        {
            this.uri = uri;
            this.jsonServices = new NodeJsonServices(uri);
        }

        #region Nodes and Node Properties
        /// <summary>
        /// Get root node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// </remarks>
        public RootNode GetRootNode()
        {
            JObject jObject = jsonServices.GetRootNode();
            RootNode root = new RootNode();            
			root.InitServices(uri);
            root.InitWithJson(jObject);
            return root;
        }   

		/// <summary>
		/// Create node
		/// </summary>
		/// <remarks>
		/// Http Success Response: 201 (node was created)
		/// Http Error Response: 400 (invalid data sent)
		/// </remarks>
		public T CreateNode<T>() where T : Node, new()
		{
			JObject jobject = jsonServices.CreateNode();
			T newNode = new T();
			newNode.InitServices(uri);
			newNode.InitWithJson(jobject);
			return newNode;
		}

        /// <summary>
        /// Get node by id
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public T GetNode<T>(int nodeId) where T : Node, new()
        {
            JObject jObject = jsonServices.GetNode(nodeId);            
            T node = new T();
			node.InitServices(uri);
            node.InitWithJson(jObject);
            return node;
        }

        /// <summary>
        /// Set properties on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (ok, no content returned)
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void SetNodeProperties<T>(T node) where T : Node
        {
			JObject props = new JObject();
			foreach (var kvp in node.GetProperties())
			{
				props.Add(kvp.Key, new JValue(kvp.Value));
			}
			jsonServices.SetNodeProperties(node.Id, props);
        }

        /// <summary>
        /// Get properties on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Success Response: 204 (ok, no properties found)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void GetNodeProperties<T>(T node) where T : Node
        {
            JObject jObject = jsonServices.GetNodeProperties(node.Id);            
            foreach (var prop in jObject)
            {
                node.SetProperty(prop.Key, prop.Value.Value<string>());
            }
        }

        /// <summary>
        /// Remove all properties on node
        /// </summary>
        /// <remarks>        
        /// Http Success Response: 204 (ok, no properties found)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void RemoveNodeProperties<T>(T node) where T : Node
        {
            this.RemoveNodeProperties(node.Id);
            node.ClearLocalProperties();
        }

        /// <summary>
        /// Remove all properties on node
        /// </summary>
        /// <remarks>        
        /// Http Success Response: 204 (ok, no properties found)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void RemoveNodeProperties(int nodeId)
        {
            jsonServices.RemoveNodeProperties(nodeId);            
        }

        /// <summary>
        /// Set property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (ok, no content returned)
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void SetNodeProperty(int nodeId, string propertyName, string propertyValue)
        {
            jsonServices.SetNodeProperty(nodeId, propertyName, propertyValue);
        }

        /// <summary>
        /// Get property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)        
        /// Http Error Response: 404 (node or property not found)
        /// </remarks>
        public string GetNodeProperty(int nodeId, string propertyName)
        {
            return jsonServices.GetNodeProperty(nodeId, propertyName);            
        }

        /// <summary>
        /// Remove property on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok)        
        /// Http Error Response: 404 (node or property not found)
        /// </remarks>
        public void RemoveNodeProperty(int nodeId, string propertyName)
        {
            jsonServices.RemoveNodeProperty(nodeId, propertyName);
        }

        /// <summary>
        /// Delete node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok)        
        /// Http Error Response: 404 (node not found)
        /// Http Error Response: 409 (Node could not be deleted (still has relationships?))
        /// </remarks>
        public void DeleteNode<T>(T node) where T : Node
        {
			jsonServices.DeleteNode(node.Id);            
            node.ClearForDelete();
        }       
        #endregion Nodes and Node Properties

        #region Node Relationships
        /// <summary>
        /// Create node relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 201 (ok, relationship was created)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 405 ("to" node, or the node specified by the URI not found)
        /// </remarks>
		public Relationship CreateRelationship<T>(T fromNode, string relationshipType, T toNode)
            where T : Node			
        {
            JObject jObject = new JObject();
        	jObject.Add("to", new JValue(toNode.SelfUri.AbsolutePath));
        	jObject.Add("data", new JObject());
			jObject.Add("type", new JValue(relationshipType));
			
			jObject = jsonServices.CreateRelationship(fromNode.Id, jObject);
			Relationship relationship = new Relationship();
			relationship.InitServices(uri);
			relationship.InitWithJson(jObject);
        	return relationship;
        }

        /// <summary>
        /// Set properties on relationship
        /// Replaces all properties on a relationship with the supplied set of properties.
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void SetRelationshipProperties(Relationship relationship)
        {
			JObject props = new JObject();
        	foreach (var kvp in relationship.GetProperties())
        	{
        		props.Add(kvp.Key, new JValue(kvp.Value));
        	}

			jsonServices.SetRelationshipProperties(relationship.Id, props);
        }

        /// <summary>
        /// Get properties on relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Success Response: 204 (a-ok, no relationships found)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
		public void GetRelationshipProperties(Relationship relationship)
        {
        	JObject jObject = jsonServices.GetRelationshipProperties(relationship.Id);			
        	foreach (var prop in jObject)
        	{				
				relationship.SetProperty(prop.Key, prop.Value.Value<string>());
        	}
        }

        /// <summary>
        /// Remove all properties on relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)        
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
		public void RemoveRelationshipProperties(Relationship relationship)
        {
            jsonServices.RemoveRelationshipProperties(relationship.Id);
			relationship.ClearLocalProperties();
        }

        /// <summary>
        /// Set a single property on relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)        
        /// Http Error Response: 400 (invalid data sent)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void SetRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Get a single property relationship
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok)
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void GetRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Remove a single property on relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)        
        /// Http Error Response: 404 (relationship or property not found)
        /// </remarks>
        public void RemoveRelationshipProperty()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Delete relationship        
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)        
        /// Http Error Response: 404 (relationship not found)
        /// </remarks>
        public void RemoveRelationship(Relationship relationship)
        {
			jsonServices.RemoveRelationship(relationship.Id);
			relationship.ClearForDelete();
        }

        /// <summary>
        /// Get relationships on node
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok, no content returned)        
        /// Http Error Response: 404 (node not found)
        /// </remarks>
		public IEnumerable<Relationship> GetRelationshipsOnNode(int nodeId, RelationshipDirection relationshipDirection, IEnumerable<string> relationshipTypes)
		{
			JArray jArray = jsonServices.GetRelationshipsOnNode(nodeId, relationshipDirection, relationshipTypes);
			List<Relationship> relationships = new List<Relationship>();
        	foreach (JObject jObject in jArray)
        	{				
				Relationship relationship = new Relationship();
				relationship.InitServices(uri);
				relationship.InitWithJson(jObject);
				relationships.Add(relationship);
        	}
        	return relationships;
		}
        #endregion Node Relationships

        #region Neo4j Indexes
        /// <summary>
        /// Get indexes
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok, no content returned)        
        /// </remarks>
        public void GetIndexes()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Add to index
        /// </summary>
        /// <remarks>
        /// Http Success Response: 201 (a-ok, indexed the node with key/value)
        /// </remarks>
        public void AddToIndex()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Remove from index
        /// </summary>
        /// <remarks>
        /// Http Success Response: 204 (a-ok, no content returned)
        /// Http Error Response: 404 (index entry not found)
        /// </remarks>
        public void RemoveFromIndex()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Query index
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok, no content returned)
        /// Http Error Response: 404 (index entry not found)
        /// </remarks>
        public void QueryIndex()
        {
            throw new NotImplementedException();
        }
        #endregion Neo4j Indexes

        #region Neo4j Graph Traverses
        /// <summary>
        /// Traverse graph
        /// </summary>
        /// <remarks>
        /// Http Success Response: 200 (a-ok, no content returned)
        /// Http Error Response: 404 (node not found)
        /// </remarks>
        public void TraverseGraph()
        {
            throw new NotImplementedException();
        }
        #endregion Neo4j Graph Traverses
	}
}